#include "Routeur.h"


Routeur::Routeur() {  
    initRouteur();
    communicationServeur();
	initThreads();
}


Routeur::Routeur(int port) {
	this->port = port;
	initRouteur();
    communicationServeur();
	initThreads();
}


Routeur::~Routeur() {}


void Routeur::initRouteur() {
    int resultat;
    socketEcoute = socket(AF_INET, SOCK_DGRAM, 0); // Création d'une socket udp 
    
    if (socketEcoute < 0) { // S'il y a une erreur
        cerr << "Impossible de créer le socket" << endl;
        exit(1);
    }
    
    myAdresse.sin_family = AF_INET;
    myAdresse.sin_addr.s_addr = htonl(INADDR_ANY); // Définition automatique de l'adresse du routeur
    myAdresse.sin_port = htons(port); // Définition du port du routeur
    
    if ((resultat = bind(socketEcoute, (struct sockaddr *) &myAdresse, sizeof(myAdresse))) < 0) { // Liaison de la socket avec le port d'écoute
        cerr << "Impossible de lier le socket en écoute ERREUR : "  << resultat <<  endl;             
        exit(1);
    }
    
    listen(socketEcoute, TAILLE_FILE_ATTENTE_ROUTEUR); // Création d'une file d'attente de taille 10
    
    adresseProcessusPerte.sin_family = AF_INET;
    adresseProcessusPerte.sin_addr.s_addr = inet_addr(ADRESSE_PROCESSUS_PERTE); // Définition automatique de l'adresse du serveur
    adresseProcessusPerte.sin_port = htons(PORT_PROCESSUS_PERTE); // Définition du port du serveur
    
    cout << "Création du routeur: " << inet_ntoa(myAdresse.sin_addr) << ":" << ntohs(myAdresse.sin_port)<< endl;
}

void Routeur::initThreads(){
	int resultat;
    
	resultat = pthread_create(&threadEnvoie, NULL, Routeur::runEnvoie, (void *)this);
	if (resultat < 0) {
		printf("\nErreur de création du thread d'envoie de message...\n");
		exit(EXIT_FAILURE);
	}    
    
	resultat = pthread_create(&threadRecoit, NULL, Routeur::runRecoit, (void*) this);
    if (resultat < 0) {
        printf("\nErreur de création du thread de de reception de message...\n");
        exit(EXIT_FAILURE);
    }
    
    resultat = pthread_create(&threadMessage, NULL, Routeur::runEnvoieMessage, (void*) this);
    if (resultat < 0) {
        printf("\nErreur de création du thread de de reception de message...\n");
        exit(EXIT_FAILURE);
    }
    
    pthread_join (threadRecoit, NULL);
    pthread_join (threadEnvoie, NULL);	
    pthread_join (threadMessage, NULL);	
}


void Routeur::communicationServeur() {
    tailleServeur = sizeof(adresseServeur);
    
    char reponseServeur[TAILLE_MESSAGE];
    memset(reponseServeur, 0x0, TAILLE_MESSAGE); // Remise à zero du message de facon à connaître le délimiteur de fin de chaîne
	
    adresseServeur.sin_family = AF_INET;
    adresseServeur.sin_port = htons(PORT_SERVEUR);
    adresseServeur.sin_addr.s_addr = inet_addr(ADRESSE_SERVEUR);
}


void Routeur::envoiePaquet(char *message,struct sockaddr* adresseDest){
	//cout << "adresse client : " << inet_ntoa(((struct sockaddr_in *) adresseDest)->sin_addr) << ":" << ntohs(((struct sockaddr_in *)adresseDest)->sin_port) << endl;
    
	if (sendto(socketEcoute, message, strlen(message), 0,  adresseDest, sizeof(struct sockaddr)) < 0) {
        cerr << "Émission du message impossible\n";
        close(socketEcoute);
        exit(1);
    }
}


void Routeur::recoitPaquet(){
	char reponseServeur[TAILLE_MESSAGE];
    memset(reponseServeur, 0x0, TAILLE_MESSAGE);
    tailleAdresseClient = sizeof(adresseClient);
    
    if (recvfrom(socketEcoute, reponseServeur, sizeof(reponseServeur), 0, (struct sockaddr *) &adresseClient, &tailleAdresseClient) < 0){ 
		cerr << "Problème de réception du messsage" << endl;
        exit(1);
    }
	//cout << "adresse client : " << inet_ntoa(adresseClient.sin_addr) << ":" << ntohs(adresseClient.sin_port) << endl;
    // cout <<endl << "Debut Message Recu :   " << reponseServeur << "    fin message Recu" <<endl;
    
    parseReponseServeur(reponseServeur);
}


void Routeur::parseReponseServeur(char *reponseServeur) {
    int nbRouteur, port;
	char *adresse, message[TAILLE_MESSAGE];
    int routeurDestination, routeurEmetteur, coutLiaison, routeurPassage, dureeVie;
    char *msg, messageNotification[TAILLE_MESSAGE];
    memset(messageNotification, 0x0, TAILLE_MESSAGE);
    strcpy(message, reponseServeur);
    msg = strtok(reponseServeur, "\n"); 
	
    if (atoi(msg) == TYPE_PAQUET_INIT){
        msg = strtok(NULL, ":");    
        idRouteur = atoi(msg);
        
        msg = strtok(NULL, ":");
        nbRouteur = atoi(msg);
        
        tableRoutage = new TableRoutage(idRouteur, nbRouteur);
        
        msg = strtok (NULL, ":");
        
        while (msg != NULL) {
            routeurDestination = atoi(msg);
            msg = strtok (NULL, ":");
            
            coutLiaison = atoi(msg);        
            msg = strtok (NULL, ":");
            if (msg != NULL) {
                adresse = msg;        
                msg = strtok (NULL, ":");
                
                port = atoi(msg);        
                msg = strtok (NULL, ":");
                
                this->adressesVoisins[routeurDestination] = *new Adresse(adresse,port);
                
            }
			attenteReponseVoisins[routeurDestination] = 0;
			if (!dejaForceModifier[routeurDestination]) {
				tableRoutage->ajouteDestination(routeurDestination, routeurDestination, coutLiaison);
			}
        }
		tableRoutage->afficher();
    }
    
    else if (atoi(reponseServeur) == TYPE_PAQUET_TABLE_ROUTAGE){
        int numRouteurDest, coutLiaison;
        
        msg = strtok(NULL, "\n");    
        dureeVie = atoi(msg);
        dureeVie--;
		
        msg = strtok(NULL, "\n");   
        routeurEmetteur = atoi(msg); // idRouteur
		msg = strtok(NULL, "\n");   
		routeurPassage = atoi(msg); // routeur intermediaire
		msg = strtok(NULL, "\n");  
		routeurDestination = atoi(msg); // routeur Destination
		
		if (routeurPassage == routeurDestination) { // Si le message est destiné a ce routeur 
			// On met a jour la table de routage
			msg =strtok(NULL,":");
			numRouteurDest = atoi(msg);
			msg =strtok(NULL,":");
			coutLiaison = atoi(msg);
			coutLiaison += tableRoutage->getTable()[routeurEmetteur].getCoutLiaison();
			
			if(attenteReponseVoisins[tableRoutage->getTable()[numRouteurDest].getNumRouteur()] >= TEMPS_CONSIDERE_MORT && !adressesVoisins[tableRoutage->getTable()[numRouteurDest].getNumRouteur()].vide() ){
				if (tableRoutage->getTable().find(numRouteurDest) != tableRoutage->getTable().end() && numRouteurDest != idRouteur) {
					tableRoutage->forceModifierDestination(numRouteurDest, routeurEmetteur, coutLiaison);
				}
			}
			
			if (tableRoutage->getTable()[numRouteurDest].getNumRouteur() == routeurEmetteur ) {
				tableRoutage->forceModifierDestination(numRouteurDest, routeurEmetteur, coutLiaison);
			}
			else {
				tableRoutage->ajouteDestination(numRouteurDest, routeurEmetteur, coutLiaison);
			}
            attenteReponseVoisins[routeurEmetteur] = 0;
			
			for (map<int,int>::iterator it = attenteReponseVoisins.begin(); it != attenteReponseVoisins.end(); it++){ 
				if (it->second >= TEMPS_CONSIDERE_MORT) {
					if (numRouteurDest == it->first && !adressesVoisins[it->first].vide() && !dejaForceModifier[it->first]) {
						dejaForceModifier[it->first] = true;
						tableRoutage->forceModifierDestination(numRouteurDest, routeurEmetteur, coutLiaison);
					}
				}
			}

			
			tableRoutage->afficher();
		}
		else { // Sinon on retransmet le message au voisin correspondant
            
            if (dureeVie > 0) {
                sprintf(msg, "%d:%d", numRouteurDest, coutLiaison);
                Paquet * paquet = new Paquet(TYPE_PAQUET_TABLE_ROUTAGE, msg, routeurEmetteur, tableRoutage->getTable()[routeurDestination].getNumRouteur(), routeurDestination, dureeVie);
                paquet->toString(message);
                
                envoiePaquet(message, getAdresseProcessusPerte());
			}
            else {
                sprintf(messageNotification, "Paquet détruit.");
                notification(messageNotification);
            }
		}
    }
    
    else if (atoi(reponseServeur) == TYPE_PAQUET_MESSAGE){
		msg = strtok(NULL,"\n");
		dureeVie = atoi(msg);
		dureeVie--;
        
		msg = strtok(NULL,"\n");
		routeurEmetteur = atoi(msg);
		
		msg = strtok(NULL,"\n");
		routeurDestination = atoi(msg);
		
		msg = strtok(NULL,"\n");
		
		if (routeurDestination == this->idRouteur){
			sprintf(messageNotification, "Message recu depuis le routeur: %d \nMessage: %s", routeurEmetteur, msg);
			notification(messageNotification);
		}
		else {
			
			if (dureeVie > 0) {
				int sommetPassage = this->tableRoutage->getRouteurPassage(routeurDestination);
				Paquet * paquet = new Paquet(TYPE_PAQUET_MESSAGE, msg, routeurEmetteur, sommetPassage, routeurDestination, dureeVie);
				paquet->toString(message);
				
				sprintf(messageNotification, "Renvoie du message:\n%s", message);
				notification(messageNotification);
				
				envoiePaquet(message, getAdresseProcessusPerte());
			}
			else {
				sprintf(messageNotification, "Paquet détruit.");
				notification(messageNotification);
			}
		}
	}
}


bool Routeur::adressesVoisinsPleine(){
	
	if (this->adressesVoisins.end() != this->adressesVoisins.begin()){
		for (map<int,Adresse>::iterator it = this->adressesVoisins.begin(); it != this->adressesVoisins.end(); it++){
			if (it->second.vide())  {
				return false;
			}
		}
		return true;
	}
	else 
		return false;
}


void Routeur::notification(char *notification) {
    
    cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
    cout << "===================================================================================" << endl;
    cout << notification; 
    cout << endl << "===================================================================================" << endl;
    cout << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl << endl;
}


void* Routeur::runEnvoie(void *arg) {
    Routeur * r = (Routeur *)arg;
	Paquet * message =new Paquet(TYPE_PAQUET_INIT);
    char paquet[2048];
    message->toString(paquet);
	r->envoiePaquet(paquet, r->getAdresseServeur());
    map<int,Adresse> adressesVoisins ;

    while (true) {
        adressesVoisins = r->getAdressesVoisins();
        for (map<int,Adresse>::iterator it = adressesVoisins.begin(); it != adressesVoisins.end(); it++){ //Pour chaque voisin dont on possede l'adresse
			if (!it->second.vide())  {
                struct sockaddr_in adresseRouteur;
                adresseRouteur.sin_family = AF_INET;
                adresseRouteur.sin_port = htons(it->second.port);
                adresseRouteur.sin_addr.s_addr = inet_addr(it->second.ip);
				
				map <int,Destination> table = r->getTableRoutage()->getTable();
				
				for (map<int,Destination>::iterator it2 = table.begin(); it2 != table.end(); it2++){ // On envoie ligne par ligne la table de routage

					char temp[2048] ;
					char heure[12];
					char message[16];
					r->getHeure(heure);
					sprintf(message, "%d:%d",it2->first,it2->second.getCoutLiaison());
					Paquet * paquet = new Paquet(TYPE_PAQUET_TABLE_ROUTAGE,message,r->getIdRouteur(),table[it->first].getNumRouteur(),it->first);
					paquet->toString(temp);
					r->envoiePaquet(temp, r->getAdresseProcessusPerte());
				}
			}
		}
        for (map<int,int>::iterator it = r->attenteReponseVoisins.begin(); it != r->attenteReponseVoisins.end(); it++){ 
			r->attenteReponseVoisins[it->first]++;
			if (r->attenteReponseVoisins[it->first] >= 50) {
				r->attenteReponseVoisins[it->first] = 	TEMPS_CONSIDERE_MORT+1;	
			}
		}
        sleep(TEMPS_ENVOIE_INFORMATION_VOISIN);
    }
    
    return NULL;
}


void* Routeur::runRecoit(void *arg) {
	Routeur * r = (Routeur *)arg;
    while (true) {
		r->recoitPaquet();
    }
    return NULL;
}


void* Routeur::runEnvoieMessage(void *arg) {
	Routeur * r = (Routeur *)arg;
    char message[TAILLE_MESSAGE];
    memset(message, 0x0,TAILLE_MESSAGE);
    
    char temp[TAILLE_MESSAGE];
    memset(temp, 0x0,TAILLE_MESSAGE);
    
    char sommetDestination[8];
    while (true) {
        cout << "Saisir le message a envoyer: ";
        cin >> message;
        cout << "Entrer l'id du routeur de destination: ";
        cin >> sommetDestination;
		
        Paquet * paquet = new Paquet(TYPE_PAQUET_MESSAGE, message, r->getIdRouteur(), r->getTableRoutage()->getRouteurPassage(atoi(sommetDestination)), atoi(sommetDestination));
        paquet->toString(temp);
        
        r->envoiePaquet(temp, r->getAdresseProcessusPerte());
    }
    
    return NULL;
}


struct sockaddr* Routeur::getAdresseServeur(){
	return (struct sockaddr *)&adresseServeur;
}


struct sockaddr* Routeur::getAdresseProcessusPerte(){
	return (struct sockaddr *)&adresseProcessusPerte;
}


map<int,Adresse> Routeur::getAdressesVoisins(){
    return this->adressesVoisins;
}


void Routeur::getHeure(char * heure){
    time_t timer;
    timer=time(NULL);
    struct tm *t = localtime(&timer);
    sprintf(heure, "%d:%d:%d",t->tm_hour,t->tm_min,t->tm_sec);
}


int Routeur::getIdRouteur(){
    return idRouteur;
}


TableRoutage * Routeur::getTableRoutage(){
    return this->tableRoutage;
}

